
use std::{net::SocketAddr, os::linux::raw::stat, path::PathBuf, sync::Arc};

//use std::path::Path;
use anyhow::Result;
use axum::{extract::{Path, State}, http::StatusCode, routing::get, Router};
use tokio::net::TcpListener;
use tracing::info;
use tower_http::services::ServeDir;


#[derive(Debug)]
struct HttpServeState {
    path: PathBuf,
}
pub async fn process_http_serve(path: PathBuf, port: u16) -> Result<()>{
    
    let addr = SocketAddr::from(([0,0,0,0],port));
    info!("Serving {:?} on  {}", path ,addr);
    let state = HttpServeState{path: path.clone()};
    // let dir_service = ServeDir::new(path)
    // .append_index_html_on_directories(true)
    // .precompressed_gzip()
    // .precompressed_br()
    // .precompressed_deflate()
    // .precompressed_zstd();
    // axum router

     
    let router = Router::new().
       route("/{*path}",get(index_handler) )
     // .nest_service("/tower", dir_service)
      .nest_service("/tower", ServeDir::new(path))
      .with_state(Arc::new(state));

    let listener = TcpListener::bind(addr).await?;
    axum::serve(listener,router).await?;  // 这里会阻塞， 等待输入
    
   
    Ok(())
}

// 这个类型 还确实 不太懂， 为什么要加  &'static ， 这个只是生命周期
async fn index_handler( 
     State(state): State<Arc<HttpServeState>>
   , Path(path): Path<String>) 
   -> (StatusCode, String){
   let p = std::path::Path::new(&state.path).join(path);
   info!("Reading file {:?}", p);
   if !p.exists(){
    return (
        StatusCode::NOT_FOUND, 
        format!("File {} not found", p.display()),
         );
   }else{
    //let content = tokio::fs::read_to_string(p).await.unwrap();
    match tokio::fs::read_to_string(&p).await {

        Ok(contents) => (StatusCode::OK, contents),
        // 因为是读为String, 所以当读  其他转不了String的， 就会报内部错， 如二进制文件， 就读不了
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to read {}: {}", p.display(), e)),
    }
   }
}

#[cfg(test)]
mod test {
    use super::*;

    #[tokio::test]
    async fn test_file_handler(){
        //let state = HttpServeState{path: path.clone()};
        let state = Arc::new(HttpServeState{path: PathBuf::from(".")});
        let (status,content) = index_handler(State(state), Path("Cargo.toml".to_string())).await;

        assert_eq!(status,StatusCode::OK);
        assert!(content.trim().contains("axum"))
    }
}